using Abp.Application.Services.Dto;
using Abp.Auditing;
using Abp.Authorization;
using Abp.Collections.Extensions;
using Abp.Extensions;
using Abp.Linq.Extensions;
using Abp.Runtime.Session;
using Abp.UI;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Application.FileFinder.Dto;
using Mt.Site.Core.Base.Tree;
using Mt.Site.Core.Organizations.UserCache;
using Mt.Site.Core.Storage.File_;
using Mt.Site.Core.Storage.Permissions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;

namespace Mt.Site.Application.FileFinder
{
    [AbpAuthorize]
    public class FileLibraryAppService : MtSiteAppServiceBase, IFileLibraryAppService
    {
        private readonly IFileLibraryManager _fileLibraryManager;

        private readonly IUserAndUnitFinder _userAndUnitFinder;

        private readonly FileLibraryUploadHelper _fileLibraryUploadHelper;

        public FileLibraryAppService(
            IFileLibraryManager fileLibraryManager,
            IUserAndUnitFinder userAndUnitFinder,
            FileLibraryUploadHelper fileLibraryUploadHelper)
        {
            _fileLibraryManager = fileLibraryManager;

            _userAndUnitFinder = userAndUnitFinder;
            _fileLibraryUploadHelper = fileLibraryUploadHelper;
        }

        #region 文件操作


        // 删除 (删除方法里执行的操作太多，一次只删一个，避免因为一次删太多导致事务卡顿，批量删除就批量发起请求)
        [AbpAuthorize(FilePermissions.Pages_Library_Delete, FilePermissions.Pages_Library_Delete_Self, RequireAllPermissions = false)]
        [HttpPost]
        public async Task DeleteFileFromLibraryAsync(EntityDto<long> input)
        {
            var hasPermission = await IsGrantedAsync(FilePermissions.Pages_Library_Delete);
            if(hasPermission)
            {
                await _fileLibraryManager.DeleteFileFromLibraryAsync(input.Id);
            }
            else
            {
                await _fileLibraryManager.DeleteFileFromLibraryByUserIdAsync(input.Id, AbpSession.GetUserId());
            }
        }


        // 移动  移动到其他文件夹
        [AbpAuthorize(FilePermissions.Pages_Library_Delete, FilePermissions.Pages_Library_Delete_Self, RequireAllPermissions = false)]
        [HttpPost]
        public async Task<DirectoryItem> MoveToOtherDirectoryAsync(MoveOtherDirectoryInput input)
        {
            var hasPermission = await IsGrantedAsync(FilePermissions.Pages_Library_Delete);
            if (hasPermission)
            {
                var t = await _fileLibraryManager.MoveToOtherDirectory(input.FileId, input.ToDirectoryId);
                return ObjectMapper.Map<DirectoryItem>(t);
            }
            else
            {
                var t = await _fileLibraryManager.MoveToOtherDirectory(input.FileId, AbpSession.GetUserId(), input.ToDirectoryId);
                return ObjectMapper.Map<DirectoryItem>(t);
            }
        }

        // 编辑  表单
        [AbpAuthorize(FilePermissions.Pages_Library_Edit, FilePermissions.Pages_Library_Edit_Self, RequireAllPermissions = false)]
        [HttpPost]
        public async Task<LibraryFileItem> UpdateFileItemAsync(UpdateFileItemInput input)
        {
            var t = await _fileLibraryManager.FileLibraryRepository.GetAsync(input.Id);

            var hasPermission = await IsGrantedAsync(FilePermissions.Pages_Library_Edit);
            if (hasPermission)
            {
                t.FileName = input.FileName;
                t.Title = input.Title;
                t.Tags = input.Tags;
            }
            else
            {
                var dir = await _fileLibraryManager.GetDirectoriesOrNullAsync(t.DirectoryId);
                if(dir != null && _fileLibraryManager.IsCanEdit(dir, AbpSession.GetUserId()))
                {
                    t.FileName = input.FileName;
                    t.Title = input.Title;
                    t.Tags = input.Tags;
                }
            }

            return ObjectMapper.Map<LibraryFileItem>(t);
        }

        #endregion

        #region 文件夹操作

        // 创建
        [AbpAuthorize(FilePermissions.Pages_Directory_Create)]
        public async Task<DirectoryItem> CreateDirectoryAsync(CreateDirectoryInput input)
        {
            var t = await _fileLibraryManager.CreateDirectory(input.DisplayName,input.Description, input.ParentId, input.UserIds, false);
            return ObjectMapper.Map<DirectoryItem>(t);
        }

        // 重命名
        [AbpAuthorize(FilePermissions.Pages_Library_Edit, FilePermissions.Pages_Directory_Edit_Self, RequireAllPermissions = false)]
        public async Task<DirectoryItem> UpdateDirectoryAsync(UpdateDirectoryInput input)
        {
            await CheckDirectoyPermissionAsync(FilePermissions.Pages_Library_Edit, input.Id);

            var t = await _fileLibraryManager.UpdateDirectory(input.Id, input.DisplayName, input.Description, input.UserIds);
            return ObjectMapper.Map<DirectoryItem>(t);
        }

        // 重命名
        public async Task<GetDirectoryForEditOut> GetDirectoryForEditAsync(EntityDto<Guid> input)
        {
            var item = await _fileLibraryManager.DirectoriesRepository.GetAsync(input.Id);
            var dto = ObjectMapper.Map<GetDirectoryForEditOut>(item);
            if (!item.Owners.IsNullOrEmpty())
            {
                dto.UserIds = item.Owners.TryGetArryFromStrBySplit<long>(",").ToList();
            }
            else
            {
                dto.UserIds = new List<long>();
            }

            return dto;
        }

        private async Task CheckDirectoyPermissionAsync(string permissionName, Guid DirId)
        {
            var hasPermission = await IsGrantedAsync(permissionName);
            if (!hasPermission)
            {
                var dir = await _fileLibraryManager.GetDirectoriesOrNullAsync(DirId);
                if (dir == null || !_fileLibraryManager.IsCanEdit(dir, AbpSession.GetUserId()))
                {
                    throw new UserFriendlyException(L("NoDirectoryOperationPermission"));
                }
            }
        }

        // 删除
        [AbpAuthorize(FilePermissions.Pages_Library_Delete, FilePermissions.Pages_Directory_Delete_Self, RequireAllPermissions = false)]
        public async Task DeleteDirectoryAsync(EntityDto<Guid> input)
        {
            await CheckDirectoyPermissionAsync(FilePermissions.Pages_Library_Delete, input.Id);

            // 暂时只能删空文件夹
            await _fileLibraryManager.TryDeleteDirectory(input.Id);
        }

        // 上移，下移
        [AbpAuthorize(FilePermissions.Pages_Library_Edit, FilePermissions.Pages_Directory_Edit_Self, RequireAllPermissions = false)]
        public async Task UpSortDirectoryAsync(EntityDto<Guid> input)
        {
            await CheckDirectoyPermissionAsync(FilePermissions.Pages_Library_Edit, input.Id);

            await _fileLibraryManager.ChangeSort(input.Id, true);
        }

        [AbpAuthorize(FilePermissions.Pages_Library_Edit, FilePermissions.Pages_Directory_Edit_Self, RequireAllPermissions = false)]
        public async Task DownSortDirectoryAsync(EntityDto<Guid> input)
        {
            await CheckDirectoyPermissionAsync(FilePermissions.Pages_Library_Edit, input.Id);

            await _fileLibraryManager.ChangeSort(input.Id, false);
        }

        [AbpAuthorize(FilePermissions.Pages_Library_Edit, FilePermissions.Pages_Directory_Edit_Self, RequireAllPermissions = false)]
        // 移动
        public async Task MoveDirectoryAsync(MoveDirectoryInput input)
        {
            await CheckDirectoyPermissionAsync(FilePermissions.Pages_Library_Edit, input.Id);

            await _fileLibraryManager.MoveDirectoryAsync(input.Id, input.NewParentId);
        }


        #endregion


        #region 查询

        // 文件夹的层级可能有很多层，不适合拉取树结构（全展开放不下），双击打开下级 ,任何人都可以看到所有文件夹，但文件夹的编辑上传权限受限

        [DisableAuditing]
        public async Task<FindDirectoryOutput> FindSubDirectoriesAsync(FindDirectoryInput input)
        {
            var result = new FindDirectoryOutput
            {
                PathItems = new List<PathItem>()
            };

            var userId = AbpSession.GetUserId();

            result.PathItems.Add(new PathItem
            {
                DisplayName = "root",
                Id = null,
                Code = null,
                Path = "root",
                IsDefault = true
            });

            if (input.ParentId.HasValue)
            {
                var item = await _fileLibraryManager.DirectoriesRepository.GetAsync(input.ParentId.Value);
                var codes = item.Code.Split(".",StringSplitOptions.RemoveEmptyEntries);

                var pre = "";
                var codeList = new List<string>();
                for (int i = 0; i < codes.Length; i++)
                {
                    var code = codes[i];
                    if (!pre.IsNullOrEmpty())
                    {
                        pre = pre + ".";
                    }
                    pre = pre + code;

                    codeList.Add(pre);
                }

                var items = await _fileLibraryManager.DirectoriesRepository.GetAll().Where(t => codeList.Contains(t.Code)).OrderBy(t=>t.Code).ToListAsync();
                string namePre = "";
                foreach (var p in items)
                {
                    if (!namePre.IsNullOrEmpty())
                    {
                        namePre = namePre + " / ";
                    }
                    namePre = namePre + p.DisplayName;
                    result.PathItems.Add(new PathItem
                    {
                        DisplayName = p.DisplayName,
                        Id = p.Id,
                        Code = p.Code,
                        Path = namePre,
                        IsDefault = p.IsDefault,
                        ReadOnly = !_fileLibraryManager.IsCanEdit(p, userId)
                    }); 
                }
            }

            var lastPath = result.PathItems.Last();
            var list = await _fileLibraryManager.DirectoriesRepository.GetAllListAsync(t => t.ParentId == input.ParentId);
  
            result.Items = list.Select(t =>
            {
                var directoryItem = ObjectMapper.Map<DirectoryItem>(t);
                directoryItem.Order = Convert.ToInt32(CodeHelper.GetLastUnitCode(t.Code));
                directoryItem.ReadOnly = !_fileLibraryManager.IsCanEdit(t, userId);
                directoryItem.Path = lastPath.Path + " / " + directoryItem.DisplayName;
                return directoryItem;
            }).OrderBy(t => t.Order).ToList();

            return result;
        }

        [DisableAuditing]
        public async Task<List<DirectoryItem>> GetDirectoriesForSelectionAsync()
        {
            var userIdStr = $",{AbpSession.GetUserId()},";

            List<Directories> list;

            var hasPermission = await IsGrantedAsync(FilePermissions.Pages_Library_Edit);
            if (hasPermission)
            {
                list = await _fileLibraryManager.DirectoriesRepository.GetAllListAsync(t => t.IsDefault == false);
            }
            else
            {
                list = await _fileLibraryManager.DirectoriesRepository.GetAllListAsync(t => t.IsDefault == false && (t.Owners == null || t.Owners.IndexOf(userIdStr) >= 0));
            }

            TreeFormatter<Guid> storage = new TreeFormatter<Guid>();
            int deep;
            var treeRoot = storage.GetMenuTreeAndOrderby(list.Select(item => item.As<ITreeItem<Guid>>()).ToList()
                , (a, b) =>
                {
                    var aOrder = a.As<Directories>().Code;
                    var bOrder = b.As<Directories>().Code;
                    return string.Compare(aOrder, bOrder);
                }, out deep);

            var itemStorages = new List<DirectoryItem>();
            iteratorTree(itemStorages, treeRoot.Items, 0, deep);
            return itemStorages;
        }

        private void iteratorTree(List<DirectoryItem> list, IList<TreeItem> trees, int level, int deep)
        {
            foreach (var n in trees)
            {
                var treeItem = n.As<TreeItem<Guid>>().Entity.As<Directories>();
                var itemStorage = ObjectMapper.Map<DirectoryItem>(treeItem);
                itemStorage.Level = level;

                list.Add(itemStorage);
                if (n.Items.Count > 0)
                {
                    var l = level + 1;
                    iteratorTree(list, n.Items, l, deep);
                }
            }
        }

        [DisableAuditing]
        public async Task<PagedResultDto<LibraryFileItem>> FindFileListAsync(FindFileListInput input)
        {
            List<Guid> dirIds;

            var query = _fileLibraryManager.FileLibraryRepository.GetAll();
            if(!input.DirectoryCode.IsNullOrEmpty())
            {
                var dirs = await _fileLibraryManager.DirectoriesRepository.GetAllListAsync(t => t.Code.StartsWith(input.DirectoryCode));
                
                dirIds = dirs.Select(t => t.Id).ToList();

                query = query.Where(t => dirIds.Contains(t.DirectoryId));
            }

            query = query.WhereIf(!input.Filter.IsNullOrEmpty(), t => t.FileName.Contains(input.Filter) || t.Tags.Contains(input.Filter) || t.Sha1 == input.Filter);

            query = !input.Sorting.IsNullOrEmpty() ? query.OrderBy(input.Sorting) : query.OrderByDescending(t => t.CreationTime);

            var count = query.Count();
            var list = await query.PageBy(input).ToListAsync();


            // 文件夹路径
            var codeList = new HashSet<string>();
            dirIds = list.Select(t => t.DirectoryId).ToList();
            var dirCodes = await (from q in _fileLibraryManager.DirectoriesRepository.GetAll() where dirIds.Contains(q.Id) select q.Code).ToListAsync();

            foreach (var item in dirCodes)
            {
                var codes = item.Split(".", StringSplitOptions.RemoveEmptyEntries);

                var pre = "";

                for (int i = 0; i < codes.Length; i++)
                {
                    var code = codes[i];
                    if (!pre.IsNullOrEmpty())
                    {
                        pre = pre + ".";
                    }
                    pre = pre + code;

                    codeList.Add(pre);
                }
            }

            var directoriesData = await _fileLibraryManager.DirectoriesRepository.GetAll().Where(t => codeList.Contains(t.Code)).ToListAsync();

            List<LibraryFileItem> data = new List<LibraryFileItem>();
            var userId = AbpSession.GetUserId();

            foreach (var t in list)
            {
                var item = await _fileLibraryUploadHelper.FileLibraryToFileItemAsync(directoriesData, userId, t, ObjectMapper, _userAndUnitFinder, _fileLibraryManager);
                data.Add(item);
            }

            return new PagedResultDto<LibraryFileItem>(count, data);
        }

        #endregion
    }
}
